import gtk

from symbide import SettingManager, PATHS

from xml.sax.saxutils import escape, unescape
from os import listdir
import os.path
import xml.dom.minidom as minidom
from widgetOrganizer import WidgetOrganizer as W_Organizer

categories = ['shortcuts']

class WidgetOrganizer(W_Organizer):
    '''
    Extends the default WidgetOrganizer.
    '''
    
    def setCategory(self, category):
        '''
        Invokes the setCategory method of all children recursivly.
        '''
        for i in self.children:
            i.setCategory(category)
            
    def resetFile(self):
        '''
        Invokes the resetFile method of all children recursivly.
        '''
        for i in self.children:
            i.resetFile()
            
    def applyFile(self):
        '''
        Invokes the applyFile method of all children recursivly.
        '''
        for i in self.children:
            i.applyFile()
            
    def showFile(self, path):
        '''
        Invokes the showFile method of all children recursivly.
        '''
        for i in self.children:
            i.showFile(path)


class SettingChanger(WidgetOrganizer, gtk.Window):
    '''
    The TopWindow for the setting changings.
    '''
    
    def __init__(self, mainWindow):
        gtk.Window.__init__(self)
        self.set_default_size(600, 400)
        WidgetOrganizer.__init__(self, 'SettingChanger')
        
        self.set_transient_for(mainWindow)
        self.set_title('Symbide - Settings')
        
        self.connect('delete_event', self._event_delete)
        self.connect('destroy', self._event_delete)
        
        child = MainVBox(self)
        
        self.add(child)
        self.addChild(child)
        self.setCategory(categories[0])
        
    def _event_delete(self, *args):
        SettingManager.refresh()


class MainVBox(WidgetOrganizer, gtk.VBox):
    '''
    Separates the window in three parts. A Choose Bar, where you can
    select which settings you want to view. Then a widget called
    Viewer, showing these settings and a ActionBar where you can
    save the changings or undo them.
    '''
    
    def __init__(self, ancestor):
        gtk.VBox.__init__(self)
        WidgetOrganizer.__init__(self, 'MainVBox', ancestor)
        
        children = ((ChooseBar(self), False),
                    (Viewer(self), True),
                    (ActionBar(self), False))
        
        for child, expand in children:
            self.pack_start(child, expand=expand)
            self.addChild(child)
            
            
class ChooseBar(WidgetOrganizer, gtk.HBox):
    '''
    Displays the name of all available settings and lets the user pick
    the type of settings he wants to edit.
    '''
    
    def __init__(self, ancestor):
        gtk.HBox.__init__(self)
        WidgetOrganizer.__init__(self, 'ChooseBar', ancestor)
        
        for c in categories:
            b = gtk.Button(c)
            b.show()
            def changeCategory(button, category):
                self.getAncestor('SettingChanger').setCategory(category)
            b.connect('clicked', changeCategory, c)
            b.set_relief(gtk.RELIEF_NONE)
            b.set_focus_on_click(False)    
            self.pack_start(b, expand=False, fill=False, padding=1)


class Viewer(WidgetOrganizer, gtk.Frame):
    '''
    In this widget the actual choosen settings are displayed.
    '''
    
    def __init__(self, ancestor):
        gtk.Frame.__init__(self)
        WidgetOrganizer.__init__(self, 'SettingsViewer', ancestor)
        
        child = ViewerVBox(self)
        self.add(child)
        self.addChild(child)


class ActionBar(WidgetOrganizer, gtk.HBox):
    '''
    Holds buttons to save and revert settings and also a close Button.
    '''
    
    def __init__(self, ancestor):
        gtk.HBox.__init__(self)
        WidgetOrganizer.__init__(self, 'ActionBar', ancestor)
        
        stocks = [gtk.STOCK_CLOSE, gtk.STOCK_APPLY, gtk.STOCK_REVERT_TO_SAVED]
        connections = [self._event_close, self._event_apply, self._event_reset]
        buttons = [gtk.Button(stock=s) for s in stocks]
        
        for b in buttons:
            b.show()
            b.set_focus_on_click(False)
            self.pack_end(b, expand=False, fill=False)
            b.connect('clicked', connections.pop(0))
            
        emptyLabel = gtk.Label('')
        emptyLabel.show()
        self.pack_start(emptyLabel, expand=True)
        
    def _event_close(self, *args):
        self.getAncestor('SettingChanger').destroy()
 
    def _event_reset(self, button):
        self.getAncestor('SettingsChanger').resetFile()
        
    def _event_apply(self, button):
        self.getAncestor('SettingsChanger').applyFile()
        SettingManager.load()
        
        
class ViewerVBox(WidgetOrganizer, gtk.VBox):
    '''
    Splits the Viewer up into a SettingsFileChooser, where
    the user can choose which file of this settings he want to edit,
    and a Display where the settings of this file are shown.
    '''
    
    def __init__(self, ancestor):
        gtk.VBox.__init__(self)
        WidgetOrganizer.__init__(self, 'SettingsVBox', ancestor)
        
        children = [[SettingsFileChooser(self), False],
                    [Display(self), True],]
        
        for child, expand in children:
            self.pack_start(child, expand=expand)
            self.addChild(child)
        
        
class SettingsFileChooser(WidgetOrganizer, gtk.HBox):
    '''
    Lets the user choose the setting file he wants to configure.
    '''
    
    def __init__(self, ancestor):
        gtk.HBox.__init__(self)
        WidgetOrganizer.__init__(self, 'SettingsFileChooser', ancestor)
        
        self.files = []
        
        self.ComboBox = gtk.combo_box_new_text()
        self.ComboBox.show()
        
        self.ComboBox.connect('changed', self._event_chooseFile)
        self.pack_start(self.ComboBox)
        
    def _event_chooseFile(self, combobox):
        #TODO: improve settings manager
        if combobox.get_active_text():
            path = os.path.join(self.dir, combobox.get_active_text())
            self.ancestor.showFile(path)
        
    def setCategory(self, category):
        '''
        Changes the content of the ComboBox to adapt to the new category.
        '''
        
        self.dir = PATHS[category]
        
        for i in self.files:
            self.ComboBox.remove_text(0)
            
        
        self.files = SettingManager.getFiles(self.dir)
        for i in self.files:
            self.ComboBox.append_text(i)
        self.ComboBox.set_active(0)


class Display(WidgetOrganizer, gtk.ScrolledWindow):
    '''
    Encloses the appropriate Display for each category.
    '''
    
    def __init__(self, ancestor):
        gtk.ScrolledWindow.__init__(self)
        WidgetOrganizer.__init__(self, 'Display', ancestor)
        
        self.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
    
    def setCategory(self, category):
        self.removeChild()
        child = eval('Display_' + category)(self)
        self.add(child)
        self.addChild(child)
        

class Display_shortcuts(WidgetOrganizer, gtk.TreeView):
    '''
    A display that let the user define his own shortcuts.
    '''
    
    modifiers = ['ctrl', 'alt', 'shift']
    
    def __init__(self, ancestor):
        self.model = gtk.ListStore(str, str)
        gtk.TreeView.__init__(self, self.model)
        WidgetOrganizer.__init__(self, 'DisplayShortcuts', ancestor)
        
        self.DOM = None
        self.path = ''
        
        self.set_rules_hint(True)
        
        self.shortcuts = []
        self.changedShortcut = None
        self.changed = False
        
        self.connect('cursor-changed', self._event_changeKey)
        self.connect('key-press-event', self._event_newKey)
        self.connect('focus-out-event', self.abortChanging)
        
        renderer = gtk.CellRendererText()
        
        column1 = gtk.TreeViewColumn('Action', renderer, text=0)
        column2 = gtk.TreeViewColumn('Shortcut', renderer, text=1)
        
        self.append_column(column1)
        self.append_column(column2)
        
    def showFile(self, path):
        self.path = path
        self.shortcuts = []
        self.changedShortcuts = None
        
        self.model = gtk.ListStore(str, str)
        self.set_model(self.model)
        
        self.DOM = SettingManager.getdom(path)
        mainElement = self.DOM.documentElement
        
        for element in mainElement.childNodes:
            if element.nodeType != element.ELEMENT_NODE:
                continue
            label = ''
            name = ''
            node = None
            for node in element.childNodes:
                if node.nodeType is not node.TEXT_NODE:
                    element.unlink()
                    continue
                buf = unescape(node.data).strip(' \t\n')
                if buf:
                    keyval, modifier = gtk.accelerator_parse(buf)
                    label = gtk.accelerator_get_label(keyval, modifier)
                    name = gtk.accelerator_name(keyval, modifier)
                    node.data = name
            if not node:
                node = minidom.Document().createTextNode('')
                element.appendChild(node)
            iter = self.model.append([element.getAttribute('description'), label])
            self.shortcuts.append([iter, node, label])
            
            
    def _event_changeKey(self, treeview):
        self.abortChanging()
        self.changedShortcut = treeview.get_cursor()[0][0]
        self.model.set_value(self.shortcuts[self.changedShortcut][0], 1, 'new shortcut')
        
    def _event_newKey(self, widget, event):
        if self.changedShortcut==None:
            return False
        masks = gtk.accelerator_get_default_mod_mask()
        
        if not event.state & masks:
            return True
        
        name = gtk.accelerator_name(event.keyval, event.state & masks)
        label = gtk.accelerator_get_label(event.keyval, event.state & masks)
        self.shortcuts[self.changedShortcut][2] = label
        self.shortcuts[self.changedShortcut][1].data = name
        self.abortChanging()
        return True
    
    def abortChanging(self, *args):
        if self.changedShortcut!=None:
            self.model.set_value(self.shortcuts[self.changedShortcut][0], 1, self.shortcuts[self.changedShortcut][2])
            self.changedShortcut = None
            
    def applyFile(self):
        if self.DOM and self.path:
            SettingManager.savedom(self.DOM, self.path)
            
    def resetFile(self):
        if self.DOM:
            self.showFile(self.path)

if __name__=='__main__':
       a=SettingChanger(None)
       a.connect('delete_event', gtk.main_quit)
       gtk.main()